import 'dart:async';

import 'package:dio/dio.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_widgets_pc/utils/log.dart';
import 'package:flutter_widgets_pc/utils/toast_util.dart';
import 'package:flutter_widgets_pc/network/response.dart';

import '../resources/l10n/l10n.dart';
import '/network/http.dart';

typedef ResponseCallback<T> = void Function(ApiResponse<T> response);
typedef RequestSuccess<T> = void Function(T data);
typedef RequestFailed = void Function({Error? e, String? msg});

class Api {
  /// GET异步请求
  ///
  /// 请求[uri]接口，[headers]指定请求头，[queryParameters]指定请求参数。
  /// 通过[fromJson]对接口返回的数据进行反序列化，得到泛型[T]类对象。
  ///
  /// 默认接口请求不展示loading框，如果需要请设置[showLoading]为true，还可通过[showLoadingDelay]设置loading延迟展示时间，
  /// 在[showLoadingDelay]期间内接口请求完成，则loading不展示。
  ///
  /// 当接口请失败时，默认吐司显示请求失败的错误消息，如果要关闭请设置[showErrorMsg]为false。
  /// 如果需要取消接口，可设置[cancelToken]，想要批量取消请求，[cancelToken]可设置为[CancelTokenGroup]，
  /// 当请求被取消时，[responseCallback]将不执行。
  static void getAsync<T>(String uri,
      {required T Function(dynamic json) fromJson,
      required ResponseCallback<T> responseCallback,
      Map<String, dynamic>? queryParameters,
      showLoading = false,
      showLoadingDelay = 300,
      showErrorMsg = true,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken}) {
    get(uri,
            fromJson: fromJson,
            queryParameters: queryParameters,
            showLoading: showLoading,
            showLoadingDelay: showLoadingDelay,
            showErrorMsg: showErrorMsg,
            headers: headers,
            cancelToken: cancelToken)
        .then(responseCallback)
        .onError((error, _) => Log.e("GET 请求被取消：$uri"));
  }

  /// POST请求
  ///
  /// 请求[uri]接口，[headers]指定请求头，通过[data]传递数据到后台。
  /// 如果关心返回的data数据，则传递对应实体的[fromJson]方法即可。
  ///
  /// 默认接口请求不展示loading框，如果需要请设置[showLoading]为true，还可通过[showLoadingDelay]设置loading延迟展示时间，
  /// 在[showLoadingDelay]期间内接口请求完成，则loading不展示。
  ///
  /// 当接口请失败时，默认吐司显示请求失败的错误消息，如果要关闭请设置[showErrorMsg]为false。
  /// 如果需要取消接口，可设置[cancelToken]，想要批量取消请求，[cancelToken]可设置为[CancelTokenGroup]，
  /// 当请求被取消时，[responseCallback]将不执行。。
  static void postAsync<T>(String uri,
      {required ResponseCallback<T> responseCallback,
      Map<String, dynamic>? data,
      showLoading = false,
      showLoadingDelay = 300,
      showErrorMsg = true,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken,
      T Function(dynamic json)? fromJson}) {
    post(uri,
            data: data,
            showLoading: showLoading,
            showLoadingDelay: showLoadingDelay,
            showErrorMsg: showErrorMsg,
            headers: headers,
            cancelToken: cancelToken,
            fromJson: fromJson)
        .then(responseCallback)
        .onError((error, stackTrace) => Log.e("POST 请求被取消：$uri"));
  }

  /// GET请求
  ///
  /// 请求[uri]接口，[headers]指定请求头，[queryParameters]指定请求参数。
  /// 通过[fromJson]对接口返回的数据进行反序列化，得到泛型[T]类对象。
  ///
  /// 默认接口请求不展示loading框，如果需要请设置[showLoading]为true，还可通过[showLoadingDelay]设置loading延迟展示时间，
  /// 在[showLoadingDelay]期间内接口请求完成，则loading不展示。
  ///
  /// 当接口请失败时，默认吐司显示请求失败的错误消息，如果要关闭请设置[showErrorMsg]为false
  /// 如果需要取消接口，可设置[cancelToken]，想要批量取消请求，[cancelToken]可设置为[CancelTokenGroup]，
  /// 当请求被取消时，将抛出错误类型为[DioErrorType.cancel]的[DioError]。
  static Future<ApiResponse<T>> get<T>(String uri,
      {required T Function(dynamic json) fromJson,
      Map<String, dynamic>? queryParameters,
      showLoading = false,
      showLoadingDelay = 300,
      showErrorMsg = true,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken}) async {
    var loadingInterceptor = LoadingInterceptor(
        showLoading: showLoading, showLoadingDelay: showLoadingDelay)
      ..onRequest();
    var interceptor = _ResponseInterceptor(showErrorMsg)..onRequest();
    try {
      var response = await Http.get(uri,
          fromJson: fromJson,
          queryParameters: queryParameters,
          headers: headers,
          cancelToken: cancelToken);
      loadingInterceptor.onCancel();
      interceptor.onResponse(response);
      return response;
    } catch (e) {
      loadingInterceptor.onCancel();
      interceptor.onCancel();
      rethrow;
    }
  }

  /// POST请求
  ///
  /// 请求[uri]接口，[headers]指定请求头，通过[data]传递数据到后台。
  /// 如果关心返回的data数据，则传递对应实体的[fromJson]方法即可。
  ///
  /// 默认接口请求不展示loading框，如果需要请设置[showLoading]为true，还可通过[showLoadingDelay]设置loading延迟展示时间，
  /// 在[showLoadingDelay]期间内接口请求完成，则loading不展示。
  ///
  /// 当接口请失败时，默认吐司显示请求失败的错误消息，如果要关闭请设置[showErrorMsg]为false。
  /// 如果需要取消接口，可设置[cancelToken]，想要批量取消请求，[cancelToken]可设置为[CancelTokenGroup]，
  /// 当请求被取消时，将抛出错误类型为[DioErrorType.cancel]的[DioError]。
  static Future<ApiResponse<T>> post<T>(String path,
      {Map<String, dynamic>? data,
      showLoading = false,
      showLoadingDelay = 300,
      showErrorMsg = true,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken,
      T Function(dynamic json)? fromJson}) async {
    var loadingInterceptor = LoadingInterceptor(
        showLoading: showLoading, showLoadingDelay: showLoadingDelay)
      ..onRequest();
    var interceptor = _ResponseInterceptor(showErrorMsg)..onRequest();
    try {
      Map<String, dynamic> headerWrapper = {};
      if (headers != null) {
        headerWrapper.addAll(headers);
      }
      if (!headerWrapper.containsKey("Content-Type")) {
        headerWrapper['Content-Type'] = 'application/x-www-form-urlencoded';
      }
      var response = await Http.post(path,
          fromJson: fromJson,
          data: data,
          headers: headerWrapper,
          cancelToken: cancelToken);
      loadingInterceptor.onCancel();
      interceptor.onResponse(response);
      return response;
    } catch (e) {
      loadingInterceptor.onCancel();
      interceptor.onCancel();
      rethrow;
    }
  }
}

/// 接口请求拦截器，可在接口请求过程做一些额外操作
mixin Interceptor {
  /// 请求发生之前调用
  void onRequest() {}

  /// 接口请求被取消
  void onCancel() {}

  /// 得到请求响应之后调用
  void onResponse(ApiResponse response) {}
}

/// 处理请求过程中加载库显示与隐藏
class LoadingInterceptor with Interceptor {
  final bool showLoading;
  final int showLoadingDelay;

  /// 接口请求开始时间
  DateTime? _startTime;
  Timer? _timer;

  LoadingInterceptor({this.showLoading = true, this.showLoadingDelay = 300});

  /// 请求发生之前调用
  @override
  void onRequest() {
    _startTime = DateTime.now();
    if (showLoading) {
      if (showLoadingDelay > 0) {
        _timer = Timer(Duration(milliseconds: showLoadingDelay), () {
          if (_startTime != null) {
            EasyLoading.show(
                status: S.current.please_wait,
                maskType: EasyLoadingMaskType.clear);
          }
        });
      } else {
        EasyLoading.show(
            status: S.current.please_wait, maskType: EasyLoadingMaskType.clear);
      }
    }
  }

  /// 接口请求异常
  @override
  void onCancel() {
    _closeLoading();
  }

  /// 得到请求响应之后调用
  @override
  void onResponse(ApiResponse response) {
    _closeLoading();
  }

  /// 关闭加载弹窗
  void _closeLoading() {
    _timer?.cancel();
    _timer = null;

    if (showLoading) {
      if (showLoadingDelay <= 0 || _startTime == null) {
        EasyLoading.dismiss();
      } else {
        var apiUseTime = DateTime.now().millisecondsSinceEpoch -
            _startTime!.millisecondsSinceEpoch;
        if (apiUseTime >= showLoadingDelay) {
          // 确保关闭的是延迟打开的loading
          EasyLoading.dismiss();
        }
      }
    }
    _startTime = null;
  }
}

/// 接口响应结果处理
class _ResponseInterceptor with Interceptor {
  final bool showErrorMsg;

  _ResponseInterceptor(this.showErrorMsg);

  /// 得到请求响应之后调用
  @override
  void onResponse(ApiResponse response) {
    var code = response.code;
    if (showErrorMsg && code != ApiResponse.success) {
      KqToast.showError(
          response.msg ?? Http.mapDioErrorMsg(DioErrorType.unknown));
    }

    if (response.code == ApiResponse.otherDeviceLogin) {
      // 登录过期
    }
  }
}
